home *** CD-ROM | disk | FTP | other *** search
/ Nebula 2 / Nebula Two.iso / Apps / DevTools / ClassEditor.0.4 / Source / CEAppManager.m < prev    next >
Encoding:
Text File  |  1995-06-07  |  16.0 KB  |  696 lines

  1. /* CEAppManager.m                 
  2.  *
  3.  * This is the basic controller of the App. It manages all big abstraction
  4.  * tasks for the panels, documents and connections to the outside.
  5.  *
  6.  * For interface-info see the header file. The comments in this file mostly
  7.  * cover only the real implementation details.
  8.  *
  9.  * Written by:         Thomas Engel
  10.  * Created:            23.10.1993 (Copyleft)
  11.  * Last modified:     07.05.1994
  12.  */
  13.  
  14. #import <CEAppManager.h>
  15. #import <CEClassEditor.h>
  16. #import <CEListener.h>
  17. #import <CEInfo.h>
  18. #import <Text_MiscExtensions.h>
  19. #import <MiscEmacsText.h>
  20. #import <MiscCompletionText.h>
  21.  
  22. #import <misckit/misckit.h>
  23.  
  24. @implementation CEAppManager
  25.  
  26. - appWillInit:sender
  27. {
  28.     // This is the init part we need to pass before we get the messages that
  29.     // we have to open some files.
  30.     // To be able to init a class we need the have the Preferences,
  31.  
  32.     // preferences = [CEPreferencesManager new];
  33.     
  34.     // Now lets load the docu templates
  35.     
  36.     NXStream     * aStream;
  37.     id            aPath;
  38.     id            ourListener;
  39.  
  40.     // Setup out own Listener...this is really nasty..anyway.
  41.     
  42.     [[NXApp appListener] free];
  43.  
  44.     ourListener = [[CEListener alloc] init];
  45.     [ourListener setDelegate:self];
  46.     [ourListener checkInAs:[NXApp appName]];
  47.     [ourListener addPort];
  48.  
  49.     [NXApp setAppListener:ourListener];    
  50.     
  51.     // Well now this is a litle rough....maybe we should search for those files
  52.     // inside the ~/AppInfo or even *Library/ClassEditor sections..anyway.    
  53.     
  54.     aPath = [MiscString newWithString:
  55.                 [(NXBundle *)[NXBundle mainBundle] directory]];
  56.     [aPath cat:"/CEClassDocuTemplate.rtf"];
  57.     if( [aPath doesExistInFileSystem] )
  58.     {
  59.         aStream = NXMapFile( [aPath stringValue], NX_READONLY );
  60.         [classDocTemplateView readRichText:aStream];
  61.         NXCloseMemory( aStream, NX_FREEBUFFER );
  62.     }
  63.     [aPath free];
  64.  
  65.     aPath = [MiscString newWithString:
  66.                 [(NXBundle *)[NXBundle mainBundle] directory]];
  67.     [aPath cat:"/CEMethodDocuTemplate.rtf"];
  68.     if( [aPath doesExistInFileSystem] )
  69.     {
  70.         aStream = NXMapFile( [aPath stringValue], NX_READONLY );
  71.         [methodDocuTemplateView readRichText:aStream];
  72.         NXCloseMemory( aStream, NX_FREEBUFFER );
  73.     }
  74.     [aPath free];
  75.     
  76.     // Here comes the hack...
  77.  
  78.     // [MiscCompletionText poseAs:[Text class]];
  79.     [MiscEmacsText poseAs:[Text class]];
  80.  
  81.     // Let's see if we have the necessary defaults at hand !
  82.     // Either hardcode them...or adjust them with a DefaultsEditor
  83.     
  84.     if( ![NXApp knowsDefaultValue:"HeaderDirectories"] )
  85.         [NXApp setDefault:"HeaderDirectories" 
  86.                 to:".\t"\
  87.                    "~/Developer/Headers\t"\
  88.                    "/LocalDeveloper/Headers\t"\
  89.                    "/LocalDeveloper/Headers/misckit\t"\
  90.                    "/NextDeveloper/Headers/appkit\t"\
  91.                    "/NextDeveloper/Headers/foundation\t"\
  92.                    "/NextDeveloper/Headers/eoaccess\t"\
  93.                    "/NextDeveloper/Headers/eointerface\t"\
  94.                    "/NextDeveloper/Headers/3Dkit\t"\
  95.                    "/NextDeveloper/Headers/indexing\t"\
  96.                    "/NextDeveloper/Headers/eoaccess\t"\
  97.                    "/NextDeveloper/Headers/nikit\t"\
  98.                    "/NextDeveloper/Headers/remote\t"\
  99.                    "/NextDeveloper/Headers/dbkit\t"\
  100.                    "/NextDeveloper/Headers/soundkit"];
  101.  
  102.     
  103.     // NOTE: We might use the source pathes for header search too. Should
  104.     // take a  look at it some day..
  105.     
  106.     if( ![NXApp knowsDefaultValue:"SourceDirectories"] )
  107.         [NXApp setDefault:"SourceDirectories" 
  108.                 to:".\t"\
  109.                    "~/Developer"];
  110.  
  111.     if( ![NXApp knowsDefaultValue:"DocumentationDirectories"] )
  112.         [NXApp setDefault:"DocumentationDirectories" 
  113.                 to:".\t"\
  114.                    "./Documentation\t"\
  115.                    "/LocalDeveloper/Documentation/MiscKit\t"\
  116.                    "/NextLibrary/Documentation/NextDev/GeneralRef/02_ApplicationKit"];
  117.     
  118.     // Here we will write down the defaults which show the ASCII prettyprinting
  119.     // This DEFINITLY is stupid..it should be customizable.
  120.     
  121.     [NXApp setDefault:"text0" to:"Helvetica \\fs24 \\red0\\green0\\blue0 \\ul0"]; 
  122.     [NXApp setDefault:"text1" to:"Helvetica-Bold \\fs24 \\red0\\green0\\blue0 \\ul0"]; 
  123.     [NXApp setDefault:"text2" to:"Times-Bold \\fs28 \\red0\\green0\\blue0 \\ul0"]; 
  124.     [NXApp setDefault:"text3" to:"Helvetica \\fs24 \\red85\\green85\\blue85 \\ul0"]; 
  125.     [NXApp setDefault:"text4" to:"Ohlfs \\fs20 \\red4\\green8\\blue114 \\ul0"]; 
  126.     [NXApp setDefault:"text5" to:"Courier-Bold \\fs24 \\red0\\green0\\blue0 \\ul0"]; 
  127.     [NXApp setDefault:"text6" to:"Ohlfs \\fs20 \\red0\\green0\\blue0 \\ul0"]; 
  128.  
  129.     // Now lets get all the Prefs into the real objects
  130.     
  131.     [self _readPreferences];
  132.     return self;
  133. }
  134.  
  135. - appDidInit:sender
  136. {
  137.     // This is some kind of late init. These object are need for working but
  138.     // not for reading the classes or having a class initialized.
  139.  
  140.     
  141.     return self;
  142. }
  143.  
  144. - (int)app:sender openFile:(const char *)path type:(const char *)type
  145. {
  146.     // This method is performed whenever a user opens a document from the
  147.     // Workspace Manager.
  148.     // We use our default file open method with some default params.
  149.     
  150.     return [self openFile:path onHost:"*" atTrueLine:0];
  151. }
  152.  
  153. - (BOOL)appAcceptsAnotherFile:sender
  154. {
  155.     // Inform the workspace that we can open multiple files.
  156.     return YES;
  157. }
  158.  
  159. - appDidBecomeActive:sender
  160. {
  161.     return self;
  162. }
  163.  
  164. - appWillTerminate:sender
  165. {
  166.     // Now lets see what we have...ask every window what is going on.
  167.  
  168.     BOOL    ready;
  169.     int     i;
  170.     id    windowList;
  171.     int    count;
  172.     int    result;
  173.  
  174.     windowList = [NXApp windowList];
  175.     count = 0;
  176.  
  177.     for( i=0; i<[windowList count]; i++ )
  178.         if( [[windowList objectAt:i] isDocEdited] ) count++;
  179.  
  180.     // Well if there are windows with unsaved contents we should tell the
  181.     // user about it.
  182.     // We won't leave until the user has decided what to do on the top level.
  183.  
  184.     if( count == 0 ) return self;
  185.  
  186.     ready = NO;
  187.     while( !ready )
  188.     {
  189.         result = NXRunAlertPanel( "Quit", "There are edited windows.", 
  190.                                 "Review Unsaved", "Quit Anyway", "Cancel" );
  191.  
  192.         if( result == NX_ALERTOTHER )
  193.             return nil;
  194.     
  195.         else if( result == NX_ALERTDEFAULT )
  196.             ready = [self _closeAllWindows];
  197.  
  198.         else ready = YES;
  199.     }
  200.     return self;
  201. }
  202.  
  203. - (BOOL)_closeAllWindows
  204. {
  205.     // We will try to close all windows and if we succeed we will let the app
  206.     // know. YES = all closed
  207.     // NOTE: Well this is kind of ugly because the app should maintain a list
  208.     //         of all the controllers that are responsible for some data. But I
  209.     //        don't want to hack down another list access stuff. So we do it
  210.     //         this way. Maybe in the future I will change that.
  211.  
  212.     id    delegateList;
  213.     id    aDelegate;
  214.     int    i;
  215.     id    windowList;
  216.     BOOL    success;
  217.  
  218.     windowList = [NXApp windowList];
  219.     delegateList = [List new];
  220.  
  221.     // Lets find all the delegates first.
  222.  
  223.     for( i=0; i<[windowList count]; i++ )
  224.     {
  225.         aDelegate = [[windowList objectAt:i] delegate];
  226.         if( aDelegate != nil &&
  227.             [aDelegate isKindOf:[CEClassEditor class]] )
  228.             [delegateList addObjectIfAbsent:aDelegate];
  229.     }
  230.     success = YES;
  231.  
  232.     for( i=0; i<[delegateList count]; i++ )
  233.     {
  234.         if( [[delegateList objectAt:i] close:self] == nil )
  235.         {
  236.             success = NO;
  237.             break;
  238.         }
  239.     }
  240.  
  241.     return success;
  242. }
  243.  
  244. - addToReleasePool:anObject
  245. {
  246.     return [NXApp delayedFree:anObject];
  247. }
  248.  
  249. - (int)openFile:(char *)path onHost:(char *)host atTrueLine:(int *)line;
  250. {
  251.     // This method is performed whenever a user opens a document.
  252.     // It takes care that every file is only opened once.
  253.     
  254.     id    newEditor;
  255.     id    aString;
  256.     id    basename;
  257.     id    filetype;
  258.     id    thePath;
  259.     id    windowList;
  260.     id    aDelegate;
  261.     int    i;
  262.     BOOL    loadIt;
  263.  
  264.     // First we have to check if we already know this file !
  265.     // Some window delegate should have the same filename (without .h or .m)
  266.     // Using the window list might not be the safest way..but I guess it
  267.     // will work.
  268.     
  269.     aString = [MiscString newWithString:path];
  270.     filetype = [aString fileExtension];
  271.     newEditor = nil;
  272.  
  273.     if( [filetype cmp:"m"] != 0 &&
  274.         [filetype cmp:"h"] != 0 &&
  275.         [filetype cmp:"rtf"] != 0 )
  276.     {
  277.         // Looks like this is no valid file !
  278.         
  279.         [aString free];
  280.         [filetype free];
  281.         return -1;
  282.     }
  283.     // Now that it is clear that this is a valid file...
  284.     
  285.     [NXApp activateSelf:NO];
  286.  
  287.     windowList = [NXApp windowList];
  288.     basename = [aString fileBasename];
  289.     thePath = [aString pathName];
  290.     [thePath addChar:[MiscString pathSeparator]];
  291.     [thePath concatenate:basename];
  292.     [basename free];
  293.     [aString free];
  294.     loadIt = YES;
  295.  
  296.     for( i=0; i<[windowList count]; i++ )
  297.     {
  298.         aDelegate = [[windowList objectAt:i] delegate];
  299.  
  300.         if( [aDelegate respondsTo:@selector(filename)] &&
  301.             aDelegate != nil )
  302.         {
  303.             if( [thePath cmp:[aDelegate filename]] == 0 )
  304.             {
  305.                 // Looks like we have found a delegate that belongs to exactly
  306.                 // the same path.
  307.                 // We must to ask this delegate now what window we should top.
  308.                 
  309.                 // If we have to open at a certain line will will skip that.
  310.                 // opening at a line will currently bring the cheat window to
  311.                 // front anyway...this prevents window cluttter.
  312.                 
  313.                 if( line == 0 )
  314.                     [[aDelegate window] makeKeyAndOrderFront:self];
  315.                 loadIt = NO;
  316.                 
  317.                 // If we have to open at a certain line we should remember
  318.                 // who is controlling that window.
  319.                 
  320.                 newEditor = aDelegate;
  321.             }
  322.         }
  323.     }
  324.     [thePath free];
  325.  
  326.       // If we really have a new editor...let the world know about it.
  327.       
  328.     if( loadIt )
  329.     {
  330.         newEditor = [[CEClassEditor alloc] initFromFile:path];
  331.         if( !newEditor )
  332.         {
  333.             [filetype free];
  334.             return NO;
  335.         }
  336.     }
  337.     
  338.     // Now lets see..do we have to select a certain line ?
  339.     // If there is on ask the editor to do so.
  340.             
  341.     if( line > 0 ) 
  342.     {
  343.         if( [filetype cmp:"m"] == 0 )
  344.             [newEditor selectTrueLine:(int)line inSource:YES];
  345.         else if( [filetype cmp:"h"] == 0 )
  346.             [newEditor selectTrueLine:(int)line inSource:NO];
  347.     }
  348.     [filetype free];
  349.     return YES;
  350. }
  351.  
  352. - showInfo:sender
  353. {
  354.     if( !info ) info = [CEInfo new];
  355.         
  356.     [info makeKeyAndOrderFront:self];
  357.     return self;
  358. }
  359.  
  360. - sendSuggestion:sender
  361. {
  362.     id    mailer;
  363.     id    tomiAdr;
  364.     id    subject;
  365.     id    theText;
  366.     
  367.     tomiAdr = [MiscString newWithString:
  368.                     "tsengel@cip.informatik.uni-erlangen.de"];
  369.     subject = [MiscString newWithString:
  370.                     "ClassEditor v0.4 suggestion"];
  371.     theText = [MiscString newWithString:
  372.                     "Here is a little suggestion on ClassEditor v0.4:\n"];
  373.  
  374.     mailer = [MiscMailApp localMailer];
  375.     [mailer setNoclutter:NO];
  376.     [mailer sendMailTo:tomiAdr subject:subject body:theText];
  377.     
  378.     [theText free];
  379.     [tomiAdr free];
  380.     [subject free];
  381.     
  382.     return self;
  383. }
  384.  
  385. - showAccessHelper:sender
  386. {
  387.     [accessHelper makeKeyAndOrderFront:self];
  388.     return self;
  389. }
  390.  
  391. - showPreferences:sender
  392. {
  393. //    if( preferences ) [preferences makeKeyAndOrderFront:self];
  394.     return self;
  395. }
  396.  
  397. - preferences
  398. {
  399.     return self;
  400. }
  401.  
  402. - _readPreferences
  403. {
  404.     id    aString;
  405.     id    someString;
  406.     id    aList;
  407.     int    i;
  408.     
  409.     aString = [MiscString newWithString:
  410.                     [NXApp defaultValue:"HeaderDirectories"]];
  411.     headerSearchPathes = [aString tokenize:"\t" into:nil];
  412.  
  413.     [aString setStringValue:[NXApp defaultValue:"SourceDirectories"]];
  414.     sourceSearchPathes = [aString tokenize:"\t" into:nil];
  415.  
  416.     // We will check the paths plus path/Classes path/Categories and
  417.     // path/Protocols
  418.     
  419.     [aString setStringValue:[NXApp defaultValue:"DocumentationDirectories"]];
  420.     aList = [aString tokenize:"\t" into:nil];
  421.     [aString free];
  422.  
  423.     docuSearchPathes = [List new];
  424.     
  425.     for( i=0; i<[aList count]; i++ )
  426.     {
  427.         aString = [[aList objectAt:i] copy];
  428.         [docuSearchPathes addObject:aString];
  429.  
  430.         someString = [aString copy];
  431.         [someString cat:"/Classes"];
  432.         [docuSearchPathes addObject:someString];
  433.  
  434.         someString = [aString copy];
  435.         [someString cat:"/Categories"];
  436.         [docuSearchPathes addObject:someString];
  437.  
  438.         someString = [aString copy];
  439.         [someString cat:"/Protocols"];
  440.         [docuSearchPathes addObject:someString];
  441.     }
  442.     [[aList freeObjects] free];
  443.     
  444.     return self;
  445. }
  446.  
  447. - headerSearchPaths
  448. {
  449.     return headerSearchPathes;
  450. }
  451.  
  452. - sourceSearchPaths
  453. {
  454.     return sourceSearchPathes;
  455. }
  456.  
  457. - documentationSearchPaths
  458. {
  459.     return docuSearchPathes;
  460. }
  461.  
  462. - open:sender
  463. {
  464.     id         openPanel;
  465.     char     fullName[MAXPATHLEN];
  466.     const char *const *files;
  467.     const char *const fileType[6] = { "m",
  468.                                       "h",
  469.                                        NULL};
  470.  
  471.      openPanel = [[OpenPanel new] allowMultipleFiles:YES];
  472.  
  473.      // Run the open panel, filtering for our type of documents
  474.      // We try to open every file the panel returns.
  475.   
  476.       if( [openPanel runModalForTypes:fileType] )
  477.       {
  478.           files = [openPanel filenames];
  479.         for( files = [openPanel filenames]; files && *files; files++)
  480.         {
  481.             // Now lets merge the fullFilename from the directory and the
  482.             // current filename and try to init a editor from this file.
  483.             // We use the default open method with some default params.
  484.               
  485.             sprintf( fullName, "%s/%s", 
  486.                     [(OpenPanel *)openPanel directory], *files );
  487.               [self openFile:fullName onHost:"*" atTrueLine:0];
  488.             }
  489.     }
  490.       return self;
  491. }
  492.  
  493. - new:sender
  494. {
  495.     [CEClassEditor new];
  496.     return self;
  497. }
  498.  
  499. - save:sender
  500. {
  501.     // [[NXApp keyWindow] save:sender];
  502.     //
  503.     // This is a posible solution
  504.     // But we currently work with a firstResponder solution
  505.     
  506.     return self;
  507. }
  508.  
  509. - saveAs:sender
  510. {
  511.     return self;
  512. }
  513.  
  514. - saveTo:sender
  515. {
  516.     return self;
  517. }
  518.  
  519. - saveAll:sender
  520. {
  521.     return self;
  522. }
  523.  
  524. - revert:sender
  525. {
  526.     return self;
  527. }
  528.  
  529. - close:sender
  530. {
  531.     return self;
  532. }
  533.  
  534. - print:sender
  535. {
  536.     return self;
  537. }
  538.  
  539. - setTextFontStyle:sender
  540. {
  541.     [self _setSelectionFont:textFontField andColor:textFontColorWell];
  542.     return self;
  543. }
  544.  
  545. - setMethodNameFontStyle:sender
  546. {
  547.     [self _setSelectionFont:methodNameField andColor:methodNameColorWell];
  548.     return self;
  549. }
  550.  
  551. - setParameterFontStyle:sender
  552. {
  553.     id firstResponder = [[NXApp mainWindow] firstResponder];
  554.     
  555.     if( [firstResponder respondsTo:@selector(setSelFont:)] )
  556.         [firstResponder setSelFont:[Font newFont:"Times-Italic" size:14]];
  557.     return self;
  558. }
  559.  
  560. - setMathSymbolFontStyle:sender
  561. {
  562.     id firstResponder = [[NXApp mainWindow] firstResponder];
  563.     
  564.     if( [firstResponder respondsTo:@selector(setSelFont:)] )
  565.         [firstResponder setSelFont:[Font newFont:"Symbol" size:14]];
  566.     return self;
  567. }
  568.  
  569. - setHeadlinesFontStyle:sender
  570. {
  571.     [self _setSelectionFont:headlinesFontField
  572.                    andColor:headlinesFontColorWell];
  573.     return self;
  574. }
  575.  
  576. - setExampleCodeFontStyle:sender
  577. {
  578.     [self _setSelectionFont:exampleCodeFontField
  579.                    andColor:exampleCodeFontColorWell];
  580.     return self;
  581. }
  582.  
  583. - setBlankLinesFontStyle:sender
  584. {
  585.     [self _setSelectionFont:blankLinesFontField
  586.                    andColor:blankLinesFontColorWell];
  587.     return self;
  588. }
  589.  
  590. - setSourceFontStyle:sender
  591. {
  592.     [self _setSelectionFont:sourceFontField andColor:sourceFontColorWell];
  593.     return self;
  594. }
  595.  
  596. - setKeywordsFontStyle:sender
  597. {
  598.     [self _setSelectionFont:keywordsFontField andColor:keywordsFontColorWell];
  599.     return self;
  600. }
  601.  
  602. - setTypecastFontStyle:sender
  603. {
  604.     [self _setSelectionFont:typecastFontField andColor:typecastFontColorWell];
  605.     return self;
  606. }
  607.  
  608. - setRemarkFontStyle:sender
  609. {
  610.     [self _setSelectionFont:remarkFontField andColor:remarkFontColorWell];
  611.     return self;
  612. }
  613.  
  614. - setBugNoteFontStyle:sender
  615. {
  616.     [self _setSelectionFont:bugNoteFontField andColor:bugNoteFontColorWell];
  617.     return self;
  618. }
  619.  
  620. - _setSelectionFont:aFont andColor:aColor
  621. {
  622.     // Well this is a lie...we don't get the font but the TextField that has
  623.     // the right font set !!
  624.     
  625.     id firstResponder = [[NXApp mainWindow] firstResponder];
  626.     
  627.     if( [firstResponder respondsTo:@selector(setSelFont:)] &&
  628.         aFont != nil )
  629.         [firstResponder setSelFont:[aFont font]];
  630.     if( [firstResponder respondsTo:@selector(setSelColor:)] &&
  631.         aColor != nil )
  632.         [firstResponder setSelColor:[aColor color]];
  633.  
  634.     // This is for letting the delegates know about the changes to
  635.     // be able to track document modification.
  636.     // The first responder must know about the window because the
  637.     // textDidChange might require it.
  638.     
  639.     if( [firstResponder respondsTo:@selector(window)] )
  640.         [[[NXApp mainWindow] delegate] textDidChange:firstResponder];
  641.  
  642.     return self;
  643. }
  644.  
  645. - copyClassDocuTemplate
  646. {
  647.     // All we do here is to copy the docu template to the pasteboard.
  648.     
  649.     [classDocTemplateView selectAll:self];
  650.     [classDocTemplateView copyTo:[Pasteboard newName:"CETempPb"]];
  651.     return self;
  652. }
  653.  
  654. - copyMethodDocuTemplate
  655. {
  656.     // All we do here is to copy the method template to the pasteboard.
  657.     
  658.     [methodDocuTemplateView selectAll:self];
  659.     [methodDocuTemplateView copyTo:[Pasteboard newName:"CETempPb"]];
  660.     return self;
  661. }
  662.  
  663. - windowDidBecomeKey:sender
  664. {
  665.     // now if our Docu window will become key...we should load the text into
  666.     // it if we havn't yet.
  667.  
  668.     NXStream     * aStream;
  669.     id            aPath;
  670.     
  671.     if( sender != docuTemplateWindow ) return self;
  672.     
  673.     if( [docuTemplateTextView textLength] < 10 )
  674.     {
  675.         aPath = [MiscString newWithString:
  676.                         [(NXBundle *)[NXBundle mainBundle] directory]];
  677.         [aPath cat:"/DocuTemplate.rtf"];
  678.         if( [aPath doesExistInFileSystem] )
  679.         {
  680.             aStream = NXMapFile( [aPath stringValue], NX_READONLY );
  681.             [docuTemplateTextView readRichText:aStream];
  682.             NXCloseMemory( aStream, NX_FREEBUFFER );
  683.         }
  684.         [aPath free];
  685.     }
  686.     return self;
  687. }
  688.  
  689. @end
  690.  
  691. /*
  692.  * History: 13.01.95 Bla.
  693.  *
  694.  *
  695.  * Bugs: No bugs and birds seen....
  696.  */